<script setup>
import { ref, onMounted, nextTick, watch } from 'vue'
import { useStore } from "@/stores/data";

// 定义 props 和 emits
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['update:modelValue'])

const store = useStore();
const keyword = ref('')
const caseSensitive = ref(false)
const editorRef = ref(null)
const isEmpty = ref(true)
const placeholder = '请输入住话术内容，匹配到违禁词将被标红并可直接删除/修改。'

// 将当前选区转换为“从编辑器起始的字符偏移”
function getEditorOffsets(root) {
  const sel = window.getSelection()
  if (!sel || sel.rangeCount === 0) return null
  const range = sel.getRangeAt(0)
  if (!root.contains(range.startContainer) || !root.contains(range.endContainer)) {
    return null
  }
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null)
  let node
  let pos = 0
  let start = null
  let end = null
  while ((node = walker.nextNode())) {
    if (node === range.startContainer) {
      start = pos + range.startOffset
    }
    if (node === range.endContainer) {
      end = pos + range.endOffset
    }
    pos += node.nodeValue.length
  }
  if (start == null || end == null) return null
  return { start, end }
}

// 根据字符偏移恢复选区
function restoreEditorOffsets(root, offsets) {
  if (!offsets) return
  const { start, end } = offsets
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null)
  let node
  let pos = 0

  let startNode = null, startOffset = 0
  let endNode = null, endOffset = 0

  while ((node = walker.nextNode())) {
    const len = node.nodeValue.length
    if (!startNode && start <= pos + len) {
      startNode = node
      startOffset = start - pos
    }
    if (!endNode && end <= pos + len) {
      endNode = node
      endOffset = end - pos
      break
    }
    pos += len
  }
  if (startNode && endNode) {
    const sel = window.getSelection()
    const range = document.createRange()
    try {
      range.setStart(startNode, Math.max(0, Math.min(startOffset, startNode.nodeValue.length)))
      range.setEnd(endNode, Math.max(0, Math.min(endOffset, endNode.nodeValue.length)))
      sel.removeAllRanges()
      sel.addRange(range)
    } catch (e) {
      // 忽略异常，防止因结构变化导致的崩溃
    }
  }
}

// 移除已有的高亮包裹
function unwrapHighlights(root) {
  const highlights = root.querySelectorAll('span.hl')
  highlights.forEach(span => {
    const parent = span.parentNode
    while (span.firstChild) parent.insertBefore(span.firstChild, span)
    parent.removeChild(span)
    parent.normalize()
  })
}

// 高亮关键词：遍历文本节点，找到匹配后用 splitText 包裹 span.hl
function highlightKeyword(root, regex) {
  if (!regex) return
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null)
  const textNodes = []
  let node
  while ((node = walker.nextNode())) {
    if (node.nodeValue && node.nodeValue.trim().length > 0) {
      textNodes.push(node)
    }
  }

  textNodes.forEach(n => {
    let current = n
    let text = current.nodeValue
    let match
    // 用循环逐步匹配并包裹
    while (text && (match = regex.exec(text))) {
      const start = match.index
      const end = start + match[0].length
      const before = current
      const mid = before.splitText(start)
      const after = mid.splitText(end - start)

      const span = document.createElement('span')
      span.className = 'hl'
      span.textContent = mid.nodeValue
      mid.parentNode.replaceChild(span, mid)

      // 下一轮从 after 开始
      current = after
      text = current.nodeValue
      // 由于我们在 after 上继续，需重置 regex.lastIndex
      regex.lastIndex = 0
    }
  })
}

// 根据 keyword 应用高亮（保存并恢复光标）
function applyHighlight() {
  const root = editorRef.value
  if (!root) return

  const offsets = getEditorOffsets(root)

  unwrapHighlights(root)
  // const kw = keyword.value
  // if (kw && kw.length > 0) {
  //   try {
  //     const flags = caseSensitive.value ? 'g' : 'gi'
  //     const pattern = new RegExp(escapeRegExp(kw), flags)
  //     highlightKeyword(root, pattern)
  //   } catch (e) {
  //     // 非法正则时忽略
  //   }
  // }
  for(let item of store.userConfig.bannedlist) {
    if (item.origin && item.origin.length > 0) {
      try {
        const flags = caseSensitive.value ? 'g' : 'gi'
        const pattern = new RegExp(escapeRegExp(item.origin), flags)
        highlightKeyword(root, pattern)
      } catch (e) {
        // 非法正则时忽略
      }
    }
  }
  nextTick(() => restoreEditorOffsets(root, offsets))
}

// 正则转义
function escapeRegExp(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

function onInput() {
  applyHighlight()
  // 更新空态和发送数据给父组件
  const root = editorRef.value
  const text = root ? (root.textContent || '') : ''
  
  // 发送更新事件给父组件
  emit('update:modelValue', text)
  
  if (root) {
    isEmpty.value = text.trim().length === 0
  }
}

function getText() {
  const root = editorRef.value
  return root ? (root.textContent || '') : ''
}

watch([keyword, caseSensitive], () => {
  applyHighlight()
})

// 监听 modelValue 变化，同步到编辑器
watch(() => props.modelValue, (newValue) => {
  const root = editorRef.value
  if (root && root.textContent !== newValue) {
    root.textContent = newValue || ''
    isEmpty.value = (newValue || '').trim().length === 0
    nextTick(() => {
      applyHighlight()
    })
  }
}, { immediate: true })

onMounted(() => {
  // 初始化编辑器内容
  const root = editorRef.value
  if (root && props.modelValue) {
    root.textContent = props.modelValue
    isEmpty.value = props.modelValue.trim().length === 0
  } else {
    isEmpty.value = true
  }
  nextTick(applyHighlight)
})

defineExpose({
  applyHighlight,
  getText
})
</script>

<template>
  <div class="wrap">
    <div class="editor">
      <div
        class="rte"
        ref="editorRef"
        contenteditable="true"
        :data-placeholder="placeholder"
        :class="{ empty: isEmpty }"
        @input="onInput"
      ></div>
    </div>
  </div>
</template>

<style scoped>
.wrap {
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
}

.toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.kw-input {
  padding: 6px 10px;
  border: 1px solid #ccc;
  border-radius: 6px;
  min-width: 220px;
}

.chk {
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
}

.editor {
  position: relative;
}

.rte {
  min-height: 160px;
  /* border: 1px solid #d6d6d6; */
  outline: none;
  line-height: 1.6;
  font-size: 14px;
  box-sizing: border-box;
  position: relative;
  /* 允许基本富文本粘贴后保持样式的前提下，避免过度样式污染 */
  word-break: break-word;
  background: #F5F6F9;
}

/* contenteditable 占位提示 */
.rte.empty:before {
  content: attr(data-placeholder);
  color: #888;
  pointer-events: none;
  position: absolute;
  left: 0;
  top: 0;
  white-space: pre-wrap;
}

/* .rte:focus {
  border-color: #646cff;
  box-shadow: 0 0 0 3px rgba(100,108,255,0.15);
} */

.hl {
  background: rgba(255, 77, 79, 0.35);
  color: #e20000;
  border-radius: 3px;
}

/* 深色模式 */
@media (prefers-color-scheme: dark) {
  .rte {
    background: #1a1a1a;
    border-color: #3a3a3a;
    color: rgba(255,255,255,0.87);
  }
  .hl {
    background: rgba(255, 77, 79, 0.45);
    color: #ffb3b3;
  }
}

.hint {
  margin-top: 8px;
  font-size: 12px;
  color: #888;
}
</style>